1 /*
2 * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.io;
27
28 import java.nio.channels.FileChannel;
29 import sun.nio.ch.FileChannelImpl;
30
31
32 /**
33 * A file output stream is an output stream for writing data to a
34 * <code>File</code> or to a <code>FileDescriptor</code>. Whether or not
35 * a file is available or may be created depends upon the underlying
36 * platform. Some platforms, in particular, allow a file to be opened
37 * for writing by only one <tt>FileOutputStream</tt> (or other
38 * file-writing object) at a time. In such situations the constructors in
39 * this class will fail if the file involved is already open.
40 *
41 * <p><code>FileOutputStream</code> is meant for writing streams of raw bytes
42 * such as image data. For writing streams of characters, consider using
43 * <code>FileWriter</code>.
44 *
45 * @author Arthur van Hoff
46 * @see java.io.File
47 * @see java.io.FileDescriptor
48 * @see java.io.FileInputStream
49 * @see java.nio.file.Files#newOutputStream
50 * @since JDK1.0
51 */
52 public
53 class FileOutputStream extends OutputStream
54 {
55 /**
56 * The system dependent file descriptor.
57 */
58 private final FileDescriptor fd;
59
60 /**
61 * True if the file is opened for append.
62 */
63 private final boolean append;
64
65 /**
66 * The associated channel, initalized lazily.
67 */
68 private FileChannel channel;
69
70 private final Object closeLock = new Object();
71 private volatile boolean closed = false;
72 private static final ThreadLocal<Boolean> runningFinalize =
73 new ThreadLocal<>();
74
75 private static boolean isRunningFinalize() {
76 Boolean val;
77 if ((val = runningFinalize.get()) != null)
78 return val.booleanValue();
79 return false;
80 }
81
82 /**
83 * Creates a file output stream to write to the file with the
84 * specified name. A new <code>FileDescriptor</code> object is
85 * created to represent this file connection.
86 * <p>
87 * First, if there is a security manager, its <code>checkWrite</code>
88 * method is called with <code>name</code> as its argument.
89 * <p>
90 * If the file exists but is a directory rather than a regular file, does
91 * not exist but cannot be created, or cannot be opened for any other
92 * reason then a <code>FileNotFoundException</code> is thrown.
93 *
94 * @param name the system-dependent filename
95 * @exception FileNotFoundException if the file exists but is a directory
96 * rather than a regular file, does not exist but cannot
97 * be created, or cannot be opened for any other reason
98 * @exception SecurityException if a security manager exists and its
99 * <code>checkWrite</code> method denies write access
100 * to the file.
101 * @see java.lang.SecurityManager#checkWrite(java.lang.String)
102 */
103 public FileOutputStream(String name) throws FileNotFoundException {
104 this(name != null ? new File(name) : null, false);
105 }
106
107 /**
108 * Creates a file output stream to write to the file with the specified
109 * name. If the second argument is <code>true</code>, then
110 * bytes will be written to the end of the file rather than the beginning.
111 * A new <code>FileDescriptor</code> object is created to represent this
112 * file connection.
113 * <p>
114 * First, if there is a security manager, its <code>checkWrite</code>
115 * method is called with <code>name</code> as its argument.
116 * <p>
117 * If the file exists but is a directory rather than a regular file, does
118 * not exist but cannot be created, or cannot be opened for any other
119 * reason then a <code>FileNotFoundException</code> is thrown.
120 *
121 * @param name the system-dependent file name
122 * @param append if <code>true</code>, then bytes will be written
123 * to the end of the file rather than the beginning
124 * @exception FileNotFoundException if the file exists but is a directory
125 * rather than a regular file, does not exist but cannot
126 * be created, or cannot be opened for any other reason.
127 * @exception SecurityException if a security manager exists and its
128 * <code>checkWrite</code> method denies write access
129 * to the file.
130 * @see java.lang.SecurityManager#checkWrite(java.lang.String)
131 * @since JDK1.1
132 */
133 public FileOutputStream(String name, boolean append)
134 throws FileNotFoundException
135 {
136 this(name != null ? new File(name) : null, append);
137 }
138
139 /**
140 * Creates a file output stream to write to the file represented by
141 * the specified <code>File</code> object. A new
142 * <code>FileDescriptor</code> object is created to represent this
143 * file connection.
144 * <p>
145 * First, if there is a security manager, its <code>checkWrite</code>
146 * method is called with the path represented by the <code>file</code>
147 * argument as its argument.
148 * <p>
149 * If the file exists but is a directory rather than a regular file, does
150 * not exist but cannot be created, or cannot be opened for any other
151 * reason then a <code>FileNotFoundException</code> is thrown.
152 *
153 * @param file the file to be opened for writing.
154 * @exception FileNotFoundException if the file exists but is a directory
155 * rather than a regular file, does not exist but cannot
156 * be created, or cannot be opened for any other reason
157 * @exception SecurityException if a security manager exists and its
158 * <code>checkWrite</code> method denies write access
159 * to the file.
160 * @see java.io.File#getPath()
161 * @see java.lang.SecurityException
162 * @see java.lang.SecurityManager#checkWrite(java.lang.String)
163 */
164 public FileOutputStream(File file) throws FileNotFoundException {
165 this(file, false);
166 }
167
168 /**
169 * Creates a file output stream to write to the file represented by
170 * the specified <code>File</code> object. If the second argument is
171 * <code>true</code>, then bytes will be written to the end of the file
172 * rather than the beginning. A new <code>FileDescriptor</code> object is
173 * created to represent this file connection.
174 * <p>
175 * First, if there is a security manager, its <code>checkWrite</code>
176 * method is called with the path represented by the <code>file</code>
177 * argument as its argument.
178 * <p>
179 * If the file exists but is a directory rather than a regular file, does
180 * not exist but cannot be created, or cannot be opened for any other
181 * reason then a <code>FileNotFoundException</code> is thrown.
182 *
183 * @param file the file to be opened for writing.
184 * @param append if <code>true</code>, then bytes will be written
185 * to the end of the file rather than the beginning
186 * @exception FileNotFoundException if the file exists but is a directory
187 * rather than a regular file, does not exist but cannot
188 * be created, or cannot be opened for any other reason
189 * @exception SecurityException if a security manager exists and its
190 * <code>checkWrite</code> method denies write access
191 * to the file.
192 * @see java.io.File#getPath()
193 * @see java.lang.SecurityException
194 * @see java.lang.SecurityManager#checkWrite(java.lang.String)
195 * @since 1.4
196 */
197 public FileOutputStream(File file, boolean append)
198 throws FileNotFoundException
199 {
200 String name = (file != null ? file.getPath() : null);
201 SecurityManager security = System.getSecurityManager();
202 if (security != null) {
203 security.checkWrite(name);
204 }
205 if (name == null) {
206 throw new NullPointerException();
207 }
208 this.fd = new FileDescriptor();
209 this.append = append;
210
211 fd.incrementAndGetUseCount();
212 open(name, append);
213 }
214
215 /**
216 * Creates a file output stream to write to the specified file
217 * descriptor, which represents an existing connection to an actual
218 * file in the file system.
219 * <p>
220 * First, if there is a security manager, its <code>checkWrite</code>
221 * method is called with the file descriptor <code>fdObj</code>
222 * argument as its argument.
223 * <p>
224 * If <code>fdObj</code> is null then a <code>NullPointerException</code>
225 * is thrown.
226 * <p>
227 * This constructor does not throw an exception if <code>fdObj</code>
228 * is {@link java.io.FileDescriptor#valid() invalid}.
229 * However, if the methods are invoked on the resulting stream to attempt
230 * I/O on the stream, an <code>IOException</code> is thrown.
231 *
232 * @param fdObj the file descriptor to be opened for writing
233 * @exception SecurityException if a security manager exists and its
234 * <code>checkWrite</code> method denies
235 * write access to the file descriptor
236 * @see java.lang.SecurityManager#checkWrite(java.io.FileDescriptor)
237 */
238 public FileOutputStream(FileDescriptor fdObj) {
239 SecurityManager security = System.getSecurityManager();
240 if (fdObj == null) {
241 throw new NullPointerException();
242 }
243 if (security != null) {
244 security.checkWrite(fdObj);
245 }
246 this.fd = fdObj;
247 this.append = false;
248
249 /*
250 * FileDescriptor is being shared by streams.
251 * Ensure that it's GC'ed only when all the streams/channels are done
252 * using it.
253 */
254 fd.incrementAndGetUseCount();
255 }
256
257 /**
258 * Opens a file, with the specified name, for overwriting or appending.
259 * @param name name of file to be opened
260 * @param append whether the file is to be opened in append mode
261 */
262 private native void open(String name, boolean append)
263 throws FileNotFoundException;
264
265 /**
266 * Writes the specified byte to this file output stream.
267 *
268 * @param b the byte to be written.
269 * @param append {@code true} if the write operation first
270 * advances the position to the end of file
271 */
272 private native void write(int b, boolean append) throws IOException;
273
274 /**
275 * Writes the specified byte to this file output stream. Implements
276 * the <code>write</code> method of <code>OutputStream</code>.
277 *
278 * @param b the byte to be written.
279 * @exception IOException if an I/O error occurs.
280 */
281 public void write(int b) throws IOException {
282 write(b, append);
283 }
284
285 /**
286 * Writes a sub array as a sequence of bytes.
287 * @param b the data to be written
288 * @param off the start offset in the data
289 * @param len the number of bytes that are written
290 * @param append {@code true} to first advance the position to the
291 * end of file
292 * @exception IOException If an I/O error has occurred.
293 */
294 private native void writeBytes(byte b[], int off, int len, boolean append)
295 throws IOException;
296
297 /**
298 * Writes <code>b.length</code> bytes from the specified byte array
299 * to this file output stream.
300 *
301 * @param b the data.
302 * @exception IOException if an I/O error occurs.
303 */
304 public void write(byte b[]) throws IOException {
305 writeBytes(b, 0, b.length, append);
306 }
307
308 /**
309 * Writes <code>len</code> bytes from the specified byte array
310 * starting at offset <code>off</code> to this file output stream.
311 *
312 * @param b the data.
313 * @param off the start offset in the data.
314 * @param len the number of bytes to write.
315 * @exception IOException if an I/O error occurs.
316 */
317 public void write(byte b[], int off, int len) throws IOException {
318 writeBytes(b, off, len, append);
319 }
320
321 /**
322 * Closes this file output stream and releases any system resources
323 * associated with this stream. This file output stream may no longer
324 * be used for writing bytes.
325 *
326 * <p> If this stream has an associated channel then the channel is closed
327 * as well.
328 *
329 * @exception IOException if an I/O error occurs.
330 *
331 * @revised 1.4
332 * @spec JSR-51
333 */
334 public void close() throws IOException {
335 synchronized (closeLock) {
336 if (closed) {
337 return;
338 }
339 closed = true;
340 }
341
342 if (channel != null) {
343 /*
344 * Decrement FD use count associated with the channel
345 * The use count is incremented whenever a new channel
346 * is obtained from this stream.
347 */
348 fd.decrementAndGetUseCount();
349 channel.close();
350 }
351
352 /*
353 * Decrement FD use count associated with this stream
354 */
355 int useCount = fd.decrementAndGetUseCount();
356
357 /*
358 * If FileDescriptor is still in use by another stream, the finalizer
359 * will not close it.
360 */
361 if ((useCount <= 0) || !isRunningFinalize()) {
362 close0();
363 }
364 }
365
366 /**
367 * Returns the file descriptor associated with this stream.
368 *
369 * @return the <code>FileDescriptor</code> object that represents
370 * the connection to the file in the file system being used
371 * by this <code>FileOutputStream</code> object.
372 *
373 * @exception IOException if an I/O error occurs.
374 * @see java.io.FileDescriptor
375 */
376 public final FileDescriptor getFD() throws IOException {
377 if (fd != null) return fd;
378 throw new IOException();
379 }
380
381 /**
382 * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
383 * object associated with this file output stream. </p>
384 *
385 * <p> The initial {@link java.nio.channels.FileChannel#position()
386 * </code>position<code>} of the returned channel will be equal to the
387 * number of bytes written to the file so far unless this stream is in
388 * append mode, in which case it will be equal to the size of the file.
389 * Writing bytes to this stream will increment the channel's position
390 * accordingly. Changing the channel's position, either explicitly or by
391 * writing, will change this stream's file position.
392 *
393 * @return the file channel associated with this file output stream
394 *
395 * @since 1.4
396 * @spec JSR-51
397 */
398 public FileChannel getChannel() {
399 synchronized (this) {
400 if (channel == null) {
401 channel = FileChannelImpl.open(fd, false, true, append, this);
402
403 /*
404 * Increment fd's use count. Invoking the channel's close()
405 * method will result in decrementing the use count set for
406 * the channel.
407 */
408 fd.incrementAndGetUseCount();
409 }
410 return channel;
411 }
412 }
413
414 /**
415 * Cleans up the connection to the file, and ensures that the
416 * <code>close</code> method of this file output stream is
417 * called when there are no more references to this stream.
418 *
419 * @exception IOException if an I/O error occurs.
420 * @see java.io.FileInputStream#close()
421 */
422 protected void finalize() throws IOException {
423 if (fd != null) {
424 if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
425 flush();
426 } else {
427
428 /*
429 * Finalizer should not release the FileDescriptor if another
430 * stream is still using it. If the user directly invokes
431 * close() then the FileDescriptor is also released.
432 */
433 runningFinalize.set(Boolean.TRUE);
434 try {
435 close();
436 } finally {
437 runningFinalize.set(Boolean.FALSE);
438 }
439 }
440 }
441 }
442
443 private native void close0() throws IOException;
444
445 private static native void initIDs();
446
447 static {
448 initIDs();
449 }
450
451 }